﻿using NUnit.Framework;

namespace State.NoPattern
{
    #region Turnstile

    public enum State { LOCKED, UNLOCKED };
    public enum Event { COIN, PASS };

    public interface ITurnstileController
    {
        void Lock();
        void Unlock();
        void Thankyou();
        void Alarm();
    }

    public class Turnstile
    {
        // Private
        internal State state = State.LOCKED;

        private ITurnstileController turnstileController;

        public Turnstile(ITurnstileController action)
        {
            turnstileController = action;
        }

        public void HandleEvent(Event e)
        {
            switch (state)
            {
                case State.LOCKED:
                    switch (e)
                    {
                        case Event.COIN:
                            state = State.UNLOCKED;
                            turnstileController.Unlock();
                            break;
                        case Event.PASS:
                            turnstileController.Alarm();
                            break;
                    }
                    break;
                case State.UNLOCKED:
                    switch (e)
                    {
                        case Event.COIN:
                            turnstileController.Thankyou();
                            break;
                        case Event.PASS:
                            state = State.LOCKED;
                            turnstileController.Lock();
                            break;
                    }
                    break;
            }
        }
    }
    
    #endregion Turnstile

    #region Tests

    [TestFixture]
    public class TurnstileTest
    {
        private Turnstile turnstile;
        private TurnstileControllerSpoof controllerSpoof;

        private class TurnstileControllerSpoof : ITurnstileController
        {
            public bool lockCalled = false;
            public bool unlockCalled = false;
            public bool thankyouCalled = false;
            public bool alarmCalled = false;
            public void Lock() { lockCalled = true; }
            public void Unlock() { unlockCalled = true; }
            public void Thankyou() { thankyouCalled = true; }
            public void Alarm() { alarmCalled = true; }
        }

        [SetUp]
        public void SetUp()
        {
            controllerSpoof = new TurnstileControllerSpoof();
            turnstile = new Turnstile(controllerSpoof);
        }

        [Test]
        public void InitialConditions()
        {
            Assert.AreEqual(State.LOCKED, turnstile.state);
        }

        [Test]
        public void CoinInLockedState()
        {
            turnstile.state = State.LOCKED;
            turnstile.HandleEvent(Event.COIN);
            Assert.AreEqual(State.UNLOCKED, turnstile.state);
            Assert.IsTrue(controllerSpoof.unlockCalled);
        }

        [Test]
        public void CoinInUnlockedState()
        {
            turnstile.state = State.UNLOCKED;
            turnstile.HandleEvent(Event.COIN);
            Assert.AreEqual(State.UNLOCKED, turnstile.state);
            Assert.IsTrue(controllerSpoof.thankyouCalled);
        }

        [Test]
        public void PassInLockedState()
        {
            turnstile.state = State.LOCKED;
            turnstile.HandleEvent(Event.PASS);
            Assert.AreEqual(State.LOCKED, turnstile.state);
            Assert.IsTrue(controllerSpoof.alarmCalled);
        }

        [Test]
        public void PassInUnlockedState()
        {
            turnstile.state = State.UNLOCKED;
            turnstile.HandleEvent(Event.PASS);
            Assert.AreEqual(State.LOCKED, turnstile.state);
            Assert.IsTrue(controllerSpoof.lockCalled);
        }
    }

    #endregion Tests
}
